﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Collections;

namespace Brio.Framework
{
    [Serializable]
    public class ScaleMask
    {
        /// <summary>
        /// Defaults to C Major Scale
        /// </summary>
     
        public ScaleMask()
        {
            _fileName = "";
            // Major Scale      C  D  E  F  G  A   B
            _mask = new int[] { 0, 2, 4, 5, 7, 9, 11 };
            // Transposition : 0 is C , 1 is C# , ... 11 is B
            _transposition = 0;
        }
        
         // Programmatic way to load scale mask
         public ScaleMask(int[] mask, int transposition, int lowerBound, int upperBound)
         {
             _fileName = "";
             _mask = mask;
             _transposition = transposition;
             _lowerBound = lowerBound;
             _upperBound = upperBound;

             ComputeKeySet();
         }
        

        // Constructor that would load a scale mask from XML
        public ScaleMask(string fileName)
        {
            // Load from file
            // 
            // ...
            //

            if (File.Exists(fileName))
            {
                _fileName = fileName;

                ReadScaleMaskFile();
            }
            else
            {
                // Throw Error
            }


        }


        public static ScaleMask CMajor
        {
            get 
            {
                // Major Scale        C  D  E  F  G  A   B
                int[] m = new int[] { 0, 2, 4, 5, 7, 9, 11 };
                // Transposition : 0 is C , 1 is C# , ... 11 is B
                int t = 0;
                // TODO: explain
                int lb = 0;
                int ub = 100; 


                return new ScaleMask(m, t, lb, ub); 
            }
        }

        public static ScaleMask CBlues
        {
            get
            {
                // Major Scale        C Eb  F  F# G  Bb  
                int[] m = new int[] { 0, 3, 5, 6, 7, 10 };
                // Transposition : 0 is C , 1 is C# , ... 11 is B
                int t = 0;
                // TODO: explain
                int lb = 0;
                int ub = 100;


                return new ScaleMask(m, t, lb, ub);
            }
        }


        public static ScaleMask EbMajor
        {
            get
            {
                // Major Scale        Eb F  G Ab  Bb  C   D
                int[] m = new int[] { 0, 2, 4, 5, 7, 9, 11 };
                // Transposition : 0 is C , 1 is C# , ... 11 is B
                int t = 4;
                // TODO: explain
                int lb = 0;
                int ub = 100;


                return new ScaleMask(m, t, lb, ub);
            }
        }



        private string _fileName;
        private int[] _mask;
        private int _transposition;
        private int _upperBound;
        private int _lowerBound;
        private int[] _keyset;


        // Up to 11 elements, No duplicates, mask value can be up to the value 11
        public int[] Mask
        {
            get { return _mask; }
        }

        // Between 0 and 11
        public int Transposition
        {
            get { return _transposition; }
        }

        // Not implemented yet
        public int LowerBound
        {
            get { return _lowerBound; }
        }

        // Not implemented yet
        public int UpperBound
        {
            get { return _upperBound; }
        }

        public int[] KeySet
        {
            get { return _keyset; }
        }

        public string FileName
        {
            get { return _fileName; }
        }

        // Beta function
        private void ComputeKeySet()
        {
            const int  OCTAVE = 12,  C4 = 60, C2 = C4 - 2*OCTAVE, C6 = C4 + 2*OCTAVE; // I do 2 octaves (each octave is 12 notes) 60 + 12 + 12 = 84 - MAGICAL MATH  

            List<int> ks = new List<int>();

            int index = C2 + Transposition; 

            bool buildingKeySet = true;

            while (buildingKeySet)
            {
                for (int i = 0; i < Mask.Length; i++)
                {
                    int k = index + Mask[i];

                    // Add to 
                    if (k <= C6)
                    {
                        ks.Add(k);
                    }
                    // Out of Range- call the whole thing off!
                    else
                    {
                        buildingKeySet = false;
                        break;
                    }
                }

                index += OCTAVE;
            }

            _keyset = ks.ToArray();
        }

        private bool ReadScaleMaskFile()
        {
            XmlDocument scaleDoc = new XmlDocument();

            scaleDoc.LoadXml(FileName);

            // Get Root Element
            XmlElement definitionElement = scaleDoc.DocumentElement;

            if (definitionElement.Name.Equals("ScaleDefinition", StringComparison.InvariantCultureIgnoreCase))
            {
                // Read Inner Nodes- Namely : The Key, Unique Name, and Mask
                foreach (XmlElement childElement in definitionElement.ChildNodes)
                {
                    if (childElement.Name.Equals("Key", StringComparison.InvariantCultureIgnoreCase))
                    {
                        string key = childElement.InnerText;
                    }
                    else if (childElement.Name.Equals("Name", StringComparison.InvariantCultureIgnoreCase))
                    {
                        string name = childElement.InnerText;
                    }
                    else if (childElement.Name.Equals("Mask", StringComparison.InvariantCultureIgnoreCase))
                    {
                        // Convert to array of strings
                        string[] stringmask = childElement.InnerText.Split(new char[] { ',', ';', ' ' });
                        int[] intmask = new int[stringmask.Length];

                        // Malformed mask
                        if (intmask.Length == 0 || intmask.Length > 11)
                            return false;

                        bool result;

                        // Get integer values
                        for (int i = 0; i < intmask.Length; i++)
                        {
                            int maskValue;

                            result = int.TryParse(stringmask[i],out maskValue);

                            if (result && maskValue > 0 && maskValue < 13)
                            {
                                intmask[i] = maskValue;
                            }
                            else
                            {
                                // Malformed mask value
                                return false;
                            }
                        }

                        // Check for duplicates
                        // TODO
                        // if duplicate -> return false; // malformed





                    }

                }

                // TODO: Were all of the required settings set?

                //_mask = intmask;

                return true;
            }
            else
            {
                return false;
            }

        }


    }



}
